#include "dbBase.h"

namespace db
{
TreeVertex* Base::Root = NULL;
TreeVertex* Base::Curr = NULL;
vector<Tag*> Base::Tags;
int Base::maxID = 0;
string Base::Pass = "abrakadabra";

Base::Base(TreeVertex* curr)
{
  // if Root doesn't exist, it means that Base haven't been initialized
  // yet; in that case, constructor will create Root, set Curr to Root
  // and maxID to 0 (freshly initialized db has only Root with ID:0)
  if (!Root)
  {
    Root = new TreeVertex(0);
    Curr = Root;
    maxID = 0;
    Tags.push_back(new Tag("all"));
  }
  // otherwise, if Root exists, the only operation is setting Curr
  // to a given TreeVertex; by default it's the old Curr, so when
  // no pointer is given - Curr remains unchanged
  else
  {
    Curr = curr;
  }
}

Base::~Base()
{
  // Deleting a database mustn't do anything; db should be reusable
}

bool Base::atRoot()
{
  // returns TRUE if current vertex is Root
  //         FALSE otherwise
  return Root == Curr;
}

bool Base::atLeaf()
{
  // returns TRUE if current vertex is a leaf
  //         FALSE otherwise
  return !(bool)Curr->Children[0];
}

void Base::setPass(string pass)
{
  Pass = pass;
}

TreeVertex* Base::addEntry()
{
  TreeVertex* vertex = new TreeVertex(++maxID, Curr); // create a new vertex
  vertex->Children[0] = Curr->Children[0]; // set new vertex's successor to current vertex's successor
  if (Curr->Children[0])                   // if current vertex's successor exists
  Curr->Children[0]->Parent = vertex; // set its parent to the new vertex
  Curr->Children[0] = vertex; // set current vertex's succesor's parent to the new vertex
  
  return vertex; // return a pointer to the new vertex
}

TreeVertex* Base::addEndEntry()
{
  TreeVertex* t = Curr;		// create temporary pointer to current vertex
  while (t->Children[0])  // as long as *t isn't a leaf
    t = t->Children[0];   // set *t to it's successor
			  // *t is now a leaf of current line

  TreeVertex* vertex = new TreeVertex(++maxID, t); // create new vertex with *t as its parent
  t->Children[0] = vertex; // set *t's successor to the new vertex
  return vertex;  // return the new vertex
}

TreeVertex* Base::addBranch()
{
  TreeVertex* branch = new TreeVertex(++maxID, Curr); // create new vertex with Curr as its parent
  Curr->Children.push_back(branch); // add the new vertes to current's children
  return branch; // return pointer to the new vertex
}

void Base::delCurrentEntry(bool children, bool successor)
{
  if (!children) // if WITH_CHILDREN flag is not set
  {
    for (int i = 1; i < Curr->getBranchCount(); i++)
    {							   // move all the current's children to
      Curr->Parent->Children.push_back(Curr->Children[i]); // its parent
      Curr->Children[i]->Parent = Curr->Parent;
      Curr->Children[i] = NULL; // and set them to NULL
    }				// to avoid their deletion
  }
  if (!successor) // if WITH_SUCCESSOR flag is not set
  {						// move current's successor to its
    Curr->Children[0]->Parent = Curr->Parent;
    if (Curr->Parent->Children[0])
      Curr->Parent->Children.push_back(Curr->Children[0]);
    else
      Curr->Parent->Children[0] = Curr->Children[0];
    Curr->Children[0] = NULL ;   // and set it to NULL to avoid its deletion
  }

  delete Curr; // delete current vertex (destructor deletes all its remaining children
  Curr = Root; // set Curr to Root
}

void Base::goTo(TreeVertex* entry)
{
  // sets Curr to given vertex
  Curr = entry;
}
		
TreeVertex* Base::moveUp()
{
  // returns a pointer to the parent of current vertex
  return Curr->Parent;
}

TreeVertex* Base::moveDown()
{
  // returns a pointer to the successor of current vertex
  return !Curr->Children[0] ? Curr : Curr->Children[0];
}

TreeVertex* Base::moveSide(int id)
{
  // returns a pointer to current vertex's child with given ID
  return !Curr->Children[id] ? Curr : Curr->Children[id];
}

TreeVertex* Base::getCurrent()
{
  return Curr;
}

Tag* Base::getTag(string tag)
{
  // find tag in vector by binary search
  int p = 0, r = Tags.size()-1, q;
  while (p != r)
  {
    q = (p+r)/2;
    if (*Tags[q] < tag)
      p = q+1;
    else
      r = q;
  }
  
  // if found tag equals given string, return its pointer
  if (*Tags[p] == tag)
    return Tags[p];
  
  // otherwise create new tag and return its pointer
  else
  {
    Tag* t = new Tag(tag);
    if (*Tags[p] > tag)
      Tags.insert(Tags.begin()+p, t);
    else
      Tags.insert(Tags.begin()+p+1, t);
    return t;
  }  
}

Tag* Base::getTag(int id)
{
  if (id < Tags.size())
    return Tags[id];
  else
    return Tags[Tags.size()-1];
}

int Base::getTagsCount()
{
  return Tags.size();
}

int Base::getCode(int ch)
{
  int seed = 0, code = 0;
  srand(seed);
  seed = Pass.length()*ch;
  srand(seed);
  for (int i = 0; i < rand()%10; i++)
    seed *= Pass[rand()%Pass.length()];
  srand(seed);
  seed = rand()*ch;  
  
  srand(seed);
  return rand()%1024;
}

int Base::Save(string filename)
{
  srand(time(NULL));
  string out = "";
  string verify = "";
  for (int i = 0; i < 479; i++)
  {
    char p = rand()%93+33;	// randomize a character from 33 to 126
	int q = (int)p+getCode(i); // add code value
	while (q > 126) q-=93; // correct coded character to fit range <33;126>
    out += (char)q; // add coded value to coded string
    verify += p; // add original character to uncoded string
  }
  cout << verify << endl << md5(verify) << endl;
  
  out += '0';
  out += md5(verify.c_str());
  
  
  FILE* file;
  file = fopen (filename.c_str(), "w");  
  fprintf(file, "%s", out.c_str());  
  fclose(file);
  
  return 0;
}

int Base::Load(string filename)
{
  string in = "", sum1, sum2; char cverify[481], csum[33];
  FILE* file;
  file = fopen (filename.c_str(), "r");
  fgets (cverify, 480, file);
  getc (file);
  fgets (csum, 33, file);
  fclose(file);
  
  for (int i = 0; i < 480; i++)
  {
	int q = (int)cverify[i] - getCode(i);
	while (q < 33) q+=93;
	cverify[i] = (char)q;
  }
  cverify[479] = '\0';
  cout << cverify << endl << endl;
  sum1 = md5(cverify);
  sum2 = csum;
  cout << sum1 << " " << sum2 << endl;
  if (sum1 != sum2)
	  return -1;
  
  goTo(Root);
  delCurrentEntry(true, true);
  Root = new TreeVertex(0);
  return 0;
}

}